package timbap.visualizer.api.context.playlist;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import timbap.visualizer.api.context.Configuration;
import timbap.visualizer.api.exceptions.NoPlaylistSelectedException;
import timbap.visualizer.api.exceptions.PlaylistParserException;
import timbap.visualizer.api.model.PlaylistEntry;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The <code>AbstractPlaylistManager</code> is the common
 * base implementation for all playlist managers.
 *
 * It specifies a generic <code>init()</code> method, while
 * leaving the methods for choosing and parsing a playlist
 * unimplemented.
 *
 * @author  Andreas Pabst
 * @see     timbap.visualizer.api.context.recorditems.RecordItemManager
 * @see     timbap.visualizer.api.model.RecordItem
 */
public abstract class AbstractPlaylistManager implements PlaylistManager {

    private static final Log LOG = LogFactory.getLog(AbstractPlaylistManager.class);

    private String playlistName;
    private List<PlaylistEntry> playlist = new ArrayList<PlaylistEntry>();
    private Map<URI,Integer> playlistIndex = new HashMap<URI, Integer>();
    private PlaylistChooser playlistChooser;

    protected abstract PlaylistEntry parsePlaylistItem(File pReferenceDirectory, String... pLinesToParse);
    
    protected AbstractPlaylistManager() {
    }
    
    protected AbstractPlaylistManager(List<PlaylistEntry> pPlaylist) {
        setPlaylist(pPlaylist);
    }

    public void init(Configuration pCfg) throws NoPlaylistSelectedException, PlaylistParserException {

		LOG.info("Initializing playlist manager ...");

		File tSelectedPlaylistFile = playlistChooser.choosePlaylistFile();
		if (tSelectedPlaylistFile == null) {
			throw new NoPlaylistSelectedException("No playlist was selected.");
		}

        List<PlaylistEntry> tPlaylist;
		LOG.info("Parsing playlist from playlist file " + tSelectedPlaylistFile + ".");
		try {
			tPlaylist = parsePlaylist(tSelectedPlaylistFile);
            LOG.debug("Found " + tPlaylist.size() + " entries in playlist file " + tSelectedPlaylistFile + ".");
            setPlaylist(tPlaylist);
		} catch (FileNotFoundException e) {
			LOG.error("Could not find selected playlist file " + tSelectedPlaylistFile.getAbsolutePath());
		} catch (IOException e) {
			LOG.error("Could not read from selected playlist file " + tSelectedPlaylistFile.getAbsolutePath(), e);
		} 

		LOG.info("Finished initializing playlist manager.");
	}

    public PlaylistChooser getPlaylistChooser() {
        return playlistChooser;
    }

    public void setPlaylistChooser(PlaylistChooser playlistChooser) {
        this.playlistChooser = playlistChooser;
    }

    public void setPlaylist(List<PlaylistEntry> pPlaylist) {
        playlist = pPlaylist;
        playlistIndex = new HashMap<URI, Integer>();
        for (int i=0; i < pPlaylist.size(); i++) {
            URI tFileUri = pPlaylist.get(i).getAudioFile().toURI();
            playlistIndex.put(tFileUri, i);
        }
    }

	public List<PlaylistEntry> getPlaylist() {
		return playlist;
	}

	public String getPlaylistName() {
		return playlistName;
	}

	public void setPlaylistName(String playlistName) {
		this.playlistName = playlistName;
	}

    public int getPlaylistSize() {
        return playlist.size();
    }

    public PlaylistEntry getPlaylistEntry(int i) {
        return playlist.get(i);
    }

    public PlaylistEntry findPlaylistEntry(URI pUri) {
        Integer tIndex = playlistIndex.get(pUri);
        if (tIndex != null) {
            return getPlaylistEntry(tIndex);
        } else {
            // check if relative URI
            tIndex = playlistIndex.get((new File(pUri.toString())).toURI());
            if (tIndex != null) {
                return getPlaylistEntry(tIndex);
            } else {
                return null;
            }
        }
    }
}
